package manager

import (
	"encoding/binary"
	"errors"
	"fmt"
	"gitee.com/kinwyb/appTools/common"
	"gitee.com/kinwyb/conv"
	"math"
	"math/rand"
	"strings"
	"sync"
	"time"
)

// Serialize 序列化
type Serialize interface {
	Marshal() []byte
	UnMarshal(data []byte) error
}

const (
	CommandServerHttpAddress byte = 5  //服务端http地址
	CommandStopProcess       byte = 7  //停止进程
	CommandStartProcess      byte = 8  //启动进程
	CommandStateProcess      byte = 12 //进程状态
	CommandUpdateProcessExec byte = 13 //更新运行程序
	CommandServerExecVersion byte = 16 //服务端软件版本
	CommandHttpRequest       byte = 17 //http请求,主要用于pprof
)

// CmdHttpRequest 发起http请求
type CmdHttpRequest struct {
	Url     string //请求地址
	Method  string //请求方式
	Body    []byte //请求内容
	Headers map[string]string
}

type CmdServerHttpAddress struct {
	Status  byte   `description:"状态"`
	Address string `description:"地址"`
}

func (c *CmdServerHttpAddress) Marshal() []byte {
	data := []byte{c.Status}
	data = append(data, []byte(c.Address)...)
	return data
}

func (c *CmdServerHttpAddress) UnMarshal(data []byte) error {
	if len(data) < 2 {
		return errors.New("无效数据")
	}
	c.Status = data[0]
	c.Address = string(data[1:])
	return nil
}

type CmdServerExecVersion struct {
	Status  byte   `description:"状态"`
	Version string `description:"地址"`
}

func (c *CmdServerExecVersion) Marshal() []byte {
	data := []byte{c.Status}
	data = append(data, []byte(c.Version)...)
	return data
}

func (c *CmdServerExecVersion) UnMarshal(data []byte) error {
	if len(data) < 2 {
		return errors.New("无效数据")
	}
	c.Status = data[0]
	c.Version = string(data[1:])
	return nil
}

type CmdProcessState struct {
	Tag       string  `description:"状态"`
	ID        string  `description:"ID"`
	Os        string  `description:"系统"`
	App       string  `description:"应用名称"`
	Version   string  `description:"运行版本"`
	IsStart   bool    `description:"状态(N|Y)"`
	StartTime int64   `description:"开始时间"`
	Mem       float64 `description:"内存使用(M)"`
	Cpu       float64 `description:"cpu占用百分比"`
}

func (c *CmdProcessState) Marshal() []byte {
	var data []byte
	if c.IsStart {
		data = append(data, 'Y')
	} else {
		data = append(data, 'N')
	}
	str := fmt.Sprintf("%s|%s|%s|%s|%s|%d|%0.2f|%0.2f", c.ID, c.Tag, c.Os, c.App, c.Version, c.StartTime, c.Mem, c.Cpu)
	data = append(data, []byte(str)...)
	return data
}

func (c *CmdProcessState) UnMarshal(data []byte) error {
	if len(data) < 1 {
		return errors.New("消息长度异常")
	}
	c.IsStart = data[0] == 'Y'
	vas := strings.Split(string(data[1:]), "|")
	if len(vas) < 5 {
		return errors.New("消息内容异常")
	}
	c.ID = vas[0]
	c.Tag = vas[1]
	c.Os = vas[2]
	c.App = vas[3]
	c.Version = vas[4]
	if len(vas) > 5 && c.IsStart {
		c.StartTime = conv.ToInt64(vas[5])
	}
	if len(vas) > 6 && c.IsStart {
		c.Mem = conv.ToFloat64(vas[6])
	}
	if len(vas) > 7 && c.IsStart {
		c.Cpu = conv.ToFloat64(vas[7])
	}
	return nil
}

// id生成器
var idGenerator *common.EtcdIDGenerator

// tcpPool
var tcpDataPool = &sync.Pool{
	New: func() interface{} {
		return &Msg{
			Done: make(chan *Msg, 1),
		}
	},
}

func init() {
	v := uint16(rand.Uint32() % uint32(math.MaxUint16))
	idGenerator = common.NewEtcdIDGenerator(v, time.Now())
}

// GetMsg 获取一个消息对象
func GetMsg() *Msg {
	return tcpDataPool.Get().(*Msg)
}

type Msg struct {
	Cmd  byte   `description:"命令"`
	ID   uint64 `description:"命令ID"`
	Data []byte `description:"命令内容"`
	Done chan *Msg
}

func (t *Msg) Marshal() []byte {
	if t.ID < 1 {
		t.ID = idGenerator.Next()
	}
	data := intToByte(int64(t.ID))
	data = append(data, t.Cmd)
	data = append(data, t.Data...)
	return data
}

func (t *Msg) UnMarshal(data []byte) error {
	if len(data) < 9 {
		return errors.New("无效数据")
	}
	t.ID = ByteToUInt(data[0:8])
	t.Cmd = data[8]
	t.Data = data[9:]
	return nil
}

// Clean 清空
func (t *Msg) Clean() {
	t.ID = 0
	t.Cmd = 0
	t.Data = nil
	tcpDataPool.Put(t)
}

// Finish 消息回复完成
func (t *Msg) Finish() {
	select {
	case t.Done <- t:
		// ok
	default:
		ServiceLog.Error("消息无法进行完成无呼叫")
		t.Clean()
	}
}

func intToByte(num int64) []byte {
	ret := make([]byte, 8)
	binary.BigEndian.PutUint64(ret, uint64(num))
	return ret
}

// ByteToUInt 字节转换成数字
func ByteToUInt(data []byte) uint64 {
	return binary.BigEndian.Uint64(data)
}
